package net.xxcxcxcx.xc_enhancedmob.mixin;

import net.minecraft.entity.EntityData;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.SpawnReason;
import net.minecraft.entity.mob.MobEntity;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.util.ActionResult;
import net.minecraft.world.LocalDifficulty;
import net.minecraft.world.ServerWorldAccess;
import net.minecraft.world.World;
import net.xxcxcxcx.xc_enhancedmob.component.BaseComponent;
import net.xxcxcxcx.xc_enhancedmob.component.BossBarComponent;
import net.xxcxcxcx.xc_enhancedmob.component.EnhancedBaseComponent;
import net.xxcxcxcx.xc_enhancedmob.data.MobData;
import net.xxcxcxcx.xc_enhancedmob.event.onAttributeCallback;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

import java.util.HashMap;
import java.util.LinkedHashMap;

@Mixin(MobEntity.class)
public abstract class MobEntityMixin extends LivingEntity implements MobData {
    private final HashMap<String, BaseComponent> MobData = new LinkedHashMap<>();

    protected MobEntityMixin(EntityType<? extends LivingEntity> entityType, World world) {
        super(entityType, world);
    }

    private static boolean haveSkill(LivingEntity livingEntity) {
        if (livingEntity instanceof MobEntity mobEntity && !mobEntity.world.isClient) {
            EnhancedBaseComponent enhancedBaseComponent = ((EnhancedBaseComponent) ((MobData) mobEntity).getComponent(EnhancedBaseComponent.class.getName()));
            return enhancedBaseComponent.hasSkills();
        }
        return false;
    }

    @Shadow
    public abstract void readCustomDataFromNbt(NbtCompound nbt);

    @Nullable
    public BaseComponent getComponent(String s) {
        return getMobData().get(s);
    }

    public void setComponent(String s, BaseComponent baseComponent) {
        getMobData().put(s, baseComponent);
    }

    public void removeCompent(String s) {
        getMobData().remove(s);
    }

    public HashMap<String, BaseComponent> getMobData() {
        return MobData;
    }

    @Inject(at = @At("TAIL"), method = "<init>")

    protected void MobEntityinitialize(EntityType entityType, World world, CallbackInfo ci) {
        setComponent(EnhancedBaseComponent.class.getName(), new EnhancedBaseComponent(((MobEntity) (Object) this)));
        setComponent(BossBarComponent.class.getName(), new BossBarComponent((MobEntity) (Object) this));
    }

    @Inject(at = @At("HEAD"), method = "readCustomDataFromNbt")
    protected void readDataFromNbt(NbtCompound nbt, CallbackInfo ci) {
        getMobData().forEach((str, component) -> component.readCustomDataFromNbt(nbt));
    }

    @Inject(at = @At("HEAD"), method = "writeCustomDataToNbt")
    protected void writeDataFromNbt(NbtCompound nbt, CallbackInfo ci) {
        getMobData().forEach((str, component) -> component.writeCustomDataToNbt(nbt));
    }

    @Inject(at = @At("HEAD"), method = "initialize")
    protected void Custominitialize(ServerWorldAccess world, LocalDifficulty difficulty, SpawnReason spawnReason, EntityData entityData, NbtCompound entityNbt, CallbackInfoReturnable<EntityData> cir) {
        getMobData().forEach((str, component) -> component.initialize(world, difficulty, spawnReason, entityData, entityNbt));
    }


    @Inject(at = @At("TAIL"), method = "initialize", cancellable = true)
    protected void ApplyAttributeAbilities(ServerWorldAccess world, LocalDifficulty difficulty, SpawnReason spawnReason, EntityData entityData, NbtCompound entityNbt, CallbackInfoReturnable<EntityData> cir) {
        if (haveSkill(this)) {
            ActionResult result = onAttributeCallback.EVENT.invoker().onAttribute(this);
            this.setHealth(this.getMaxHealth());
            if (result == ActionResult.FAIL) {
                cir.cancel();
            }
        }
    }

    @Inject(at = @At("HEAD"), method = "tick")
    protected void Customtick(CallbackInfo ci) {
        getMobData().forEach((str, component) -> component.tick());
    }

}